﻿#include <cassert>
#include "SRMacro.h"
#include "SRData.h"
#include <string.h>

namespace sora
{
#if _IPHONE_
#pragma mark kind of vertex
#endif
	Vertex3::Vertex3(const matsu::vec3 &pos, const matsu::vec4 &color, const matsu::vec2 &texCoord, const matsu::vec3 &normal)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		matsu::vec4 &colorVec = const_cast<matsu::vec4&>(color);
		matsu::vec2 &texCoordVec = const_cast<matsu::vec2&>(texCoord);
		matsu::vec3 &normalVec = const_cast<matsu::vec3&>(normal);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
		memcpy(this->color, colorVec.pointer(), 4 * sizeof(float));
		memcpy(this->texCoord, texCoordVec.pointer(), 2 * sizeof(float));
		memcpy(this->normal, normalVec.pointer(), 3 * sizeof(float));
	}
	Vertex3::Vertex3(const matsu::vec3 &pos, const matsu::vec4 &color, const matsu::vec2 &texCoord)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		matsu::vec4 &colorVec = const_cast<matsu::vec4&>(color);
		matsu::vec2 &texCoordVec = const_cast<matsu::vec2&>(texCoord);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
		memcpy(this->color, colorVec.pointer(), 4 * sizeof(float));
		memcpy(this->texCoord, texCoordVec.pointer(), 2 * sizeof(float));
		memset(normal, 0, 3 * sizeof(float));
	}
	Vertex3::Vertex3(const matsu::vec3 &pos, const matsu::vec4 &color)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		matsu::vec4 &colorVec = const_cast<matsu::vec4&>(color);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
		memcpy(this->color, colorVec.pointer(), 4 * sizeof(float));
		memset(texCoord, 0, 2 * sizeof(float));
		memset(normal, 0, 3 * sizeof(float));
	}
	Vertex3::Vertex3(const matsu::vec3 &pos)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
		memset(color, 0, 4 * sizeof(float));
		memset(texCoord, 0, 2 * sizeof(float));
		memset(normal, 0, 3 * sizeof(float));
	}

	SimpleVertex3::SimpleVertex3(const matsu::vec3 &pos)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
	}
	SimpleVertex3::SimpleVertex3(const float *ptr)
	{
		memcpy(this->pos, ptr, 3 * sizeof(float));
	}

	ModelVertex3::ModelVertex3(const matsu::vec3 &pos, const matsu::vec2 &texCoord, const matsu::vec3 &normal)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		matsu::vec2 &texCoordVec = const_cast<matsu::vec2&>(texCoord);
		matsu::vec3 &normalVec = const_cast<matsu::vec3&>(normal);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
		memcpy(this->texCoord, texCoordVec.pointer(), 2 * sizeof(float));
		memcpy(this->normal, normalVec.pointer(), 3 * sizeof(float));
	}
	ModelVertex3::ModelVertex3(const matsu::vec3 &pos, const matsu::vec2 &texCoord)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		matsu::vec2 &texCoordVec = const_cast<matsu::vec2&>(texCoord);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
		memcpy(this->texCoord, texCoordVec.pointer(), 2 * sizeof(float));
		memset(this->normal, 0, 3 * sizeof(float));
	}
	ModelVertex3::ModelVertex3(const matsu::vec3 &pos, const matsu::vec3 &normal)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		matsu::vec3 &normalVec = const_cast<matsu::vec3&>(normal);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
		memset(this->texCoord, 0, 2 * sizeof(float));
		memcpy(this->normal, normalVec.pointer(), 3 * sizeof(float));
	}
	ModelVertex3::ModelVertex3(const matsu::vec3 &pos)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
		memset(this->texCoord, 0, 2 * sizeof(float));
		memset(this->normal, 0, 3 * sizeof(float));
	}

	ColorVertex3::ColorVertex3(const matsu::vec3 &pos, const matsu::vec4 &color)
	{
		matsu::vec3 &posVec = const_cast<matsu::vec3&>(pos);
		matsu::vec4 &colorVec = const_cast<matsu::vec4&>(color);
		memcpy(this->pos, posVec.pointer(), 3 * sizeof(float));
		memcpy(this->color, colorVec.pointer(), 4 * sizeof(float));
	}
#if _IPHONE_
#pragma mark vertex pointer etc
#endif
	void VertexPointerBuilder::reset()
	{
		start_ = NULL;
		vertexOffset_ = -1;
		colorOffset_ = -1;
		texCoordOffset_ = -1;
		normalOffset_ = -1;
		stride_ = -1;
		count_ = 0;
	}
	void VertexPointerBuilder::build(VertexPointer &pointer)
	{
		SR_ASSERT(start_ != NULL);	//점조차 설정안되있는건 말이 안된다
		SR_ASSERT(stride_ >= 0);	//stride를 적절히 설정한다
		SR_ASSERT(count_ > 0);
		pointer.start_ = start_;
		pointer.vertexOffset_ = vertexOffset_;
		pointer.colorOffset_ = colorOffset_;
		pointer.texCoordOffset_ = texCoordOffset_;
		pointer.normalOffset_ = normalOffset_;
		pointer.stride_ = stride_;
		pointer.count_ = count_;
		reset();
	}
	VertexPointer VertexPointerBuilder::build()
	{
		VertexPointer ptr;
		build(ptr);
		return ptr;
	}
	int VertexPointerBuilder::calcOffset(void* p1, void *p2) const
	{
		char *ptr1 = (char*)p1;
		char *ptr2 = (char*)p2;
		int offset = ptr1 - ptr2;
		return offset;
	}
	void VertexPointerBuilder::setStartPointer(const GLvoid *start)
	{
		SR_ASSERT(start != NULL);
		start_ = const_cast<GLvoid*>(start);
	}
	void VertexPointerBuilder::setVertexOffset(int offset)
	{
		SR_ASSERT(offset >= 0);
		vertexOffset_ = offset; 
	}
	void VertexPointerBuilder::setColorOffset(int offset)
	{
		SR_ASSERT(offset >= 0);
		colorOffset_ = offset;
	}
	void VertexPointerBuilder::setTexCoordOffset(int offset)
	{
		SR_ASSERT(offset >= 0);
		texCoordOffset_ = offset;
	}
	void VertexPointerBuilder::setNormalOffset(int offset)
	{
		SR_ASSERT(offset >= 0);
		normalOffset_ = offset;
	}

	void VertexPointerBuilder::setStride(GLsizei stride)
	{
		SR_ASSERT(stride >= 0);
		stride_ = stride;
	}
	void VertexPointerBuilder::setCount(int count)
	{
		SR_ASSERT(count > 0);
		count_ = count;
	}
	VertexPointerBuilder::VertexPointerBuilder()
	{
		reset();
	}
	VertexPointer::VertexPointer()
	: start_(NULL), 
	vertexOffset_(-1),
	colorOffset_(-1),
	texCoordOffset_(-1),
	normalOffset_(-1),
	stride_(-1),
	count_(-1)
	{
	}

	const GLvoid *VertexPointer::vertex() const 
	{
		GLvoid *ptr = static_cast<char*>(start_) + vertexOffset_; 
		return ptr;
	}
	const GLvoid *VertexPointer::color() const
	{
		GLvoid *ptr = static_cast<char*>(start_) + colorOffset_; 
		return ptr;
	}
	const GLvoid *VertexPointer::texCoord() const
	{
		GLvoid *ptr = static_cast<char*>(start_) + texCoordOffset_;
		return ptr;
	}
	const GLvoid *VertexPointer::normal() const 
	{
		GLvoid *ptr = static_cast<char*>(start_) + normalOffset_; 
		return ptr;
	}
	const GLvoid *VertexPointer::start() const { return start_; }
	GLsizei VertexPointer::stride() const { SR_ASSERT(stride_ >= 0); return stride_; }
	int VertexPointer::count() const { SR_ASSERT(count_ > 0); return count_; }
	GLvoid* VertexPointer::vertexOffset() const { return (GLvoid*)vertexOffset_; }
	GLvoid*VertexPointer::colorOffset() const { return (GLvoid*)colorOffset_; }
	GLvoid*VertexPointer::texCoordOffset() const { return (GLvoid*)texCoordOffset_; }
	GLvoid*VertexPointer::normalOffset() const { return (GLvoid*)normalOffset_; }
	bool VertexPointer::useVertexPointer() const
	{
		if(vertexOffset_ == -1)
			return false;
		else
			return true;
	}
	bool VertexPointer::useColorPointer() const
	{
		if(colorOffset_ == -1)
			return false;
		else
			return true;
	}
	bool VertexPointer::useTexCoordPointer() const
	{
		if(texCoordOffset_ == -1)
			return false;
		else
			return true;
	}
	bool VertexPointer::useNormalPointer() const
	{
		if(normalOffset_ == -1)
			return false;
		else
			return true;
	}

	DrawCommand::DrawCommand(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, const VertexPointer &pointer)
	: mode_(mode), count_(count), type_(type), indices_(indices), pointer_(pointer)
	{
	}
	GLenum DrawCommand::mode() const { return mode_; }
	GLsizei DrawCommand::count() const { return count_; }
	GLenum DrawCommand::type() const { return type_; }
	const GLvoid *DrawCommand::indices() const { return indices_; }
	const VertexPointer &DrawCommand::pointer() const { return pointer_; }

	Box::Box(float top, float bottom, float left, float right, float front, float back)
	: top_(top), bottom_(bottom), left_(left), right_(right), front_(front), back_(back)
	{
		SR_ASSERT(top >= bottom);
		SR_ASSERT(left <= right);
		SR_ASSERT(front >= back);
	}

	Box::Box(const std::vector<SimpleVertex3> &vertexList)
	{
		setInvalidValue();
		vector<SimpleVertex3>::const_iterator iter;
		for(iter = vertexList.begin() ; iter != vertexList.end() ; iter++)
		{
			const SimpleVertex3 &v = *iter;
			processVertex(v.pos[0], v.pos[1], v.pos[2]);
		}
	}
	Box::Box(const std::vector<ModelVertex3> &vertexList)
	{
		setInvalidValue();
		vector<ModelVertex3>::const_iterator iter;
		for(iter = vertexList.begin() ; iter != vertexList.end() ; iter++)
		{
			const ModelVertex3 &v = *iter;
			processVertex(v.pos[0], v.pos[1], v.pos[2]);
		}
	}
	Box::Box(const std::vector<ColorVertex3> &vertexList)
	{
		setInvalidValue();
		vector<ColorVertex3>::const_iterator iter;
		for(iter = vertexList.begin() ; iter != vertexList.end() ; iter++)
		{
			const ColorVertex3 &v = *iter;
			processVertex(v.pos[0], v.pos[1], v.pos[2]);
		}
	}
	Box::Box(const std::vector<Vertex3> &vertexList)
	{
		setInvalidValue();
		vector<Vertex3>::const_iterator iter;
		for(iter = vertexList.begin() ; iter != vertexList.end() ; iter++)
		{
			const Vertex3 &v = *iter;
			processVertex(v.pos[0], v.pos[1], v.pos[2]);
		}
	}
	void Box::setInvalidValue()
	{
		top_ = -FLT_MAX;
		bottom_ = FLT_MAX;
		left_ = FLT_MAX;
		right_ = -FLT_MAX;
		front_ = -FLT_MAX;
		back_ = FLT_MAX;
	}

	void Box::processVertex(float x, float y, float z)
	{
		if(x < left_)
			left_ = x;
		if(x > right_)
			right_ = x;

		if(y > top_)
			top_ = y;
		if(y < bottom_)
			bottom_ = y;

		if(z < back_)
			back_ = z;
		if(z > front_)
			front_ = z;
	}

	Box& Box::operator+=(const Box &o)
	{
		bottom_ = bottom() < o.bottom() ? bottom() : o.bottom();
		top_ = top() > o.top() ? top() : o.top();
		left_ = left() < o.left() ? left() : o.left();
		right_ = right() > o.right() ? right() : o.right();
		front_ = front() > o.front() ? front() : o.front();
		back_ = back() < o.back() ? back() : o.back();
		return *this;
	}
	Box Box::operator+(const Box &o)
	{
		Box tmp = *this;
		tmp += o;
		return tmp;
	}

	float Box::width() const { return (right() - left()); }
	float Box::height() const { return (top() - bottom()); }
	float Box::depth() const { return (front() - back()); }

	float Box::top() const { return top_; }
	float Box::bottom() const { return bottom_; }
	float Box::left() const { return left_; }
	float Box::right() const { return right_; }
	float Box::front() const { return front_; }
	float Box::back() const { return back_; }

	color4 Color::red()
	{
		static color4 r(1.0, 0, 0, 1.0);
		return r;
	}
	color4 Color::green()
	{
		static color4 g(0, 1, 0, 1);
		return g;
	}
	color4 Color::blue()
	{
		static color4 b(0, 0, 1, 0);
		return b;
	}
	color4 Color::white()
	{
		static color4 w(1, 1, 1, 1);
		return w;
	}
	color4 Color::black()
	{
		static color4 b(0, 0, 0, 1);
		return b;
	}

}
